/****************************************************************************/
/*                   MONTAGE PROPRIETARY AND CONFIDENTIAL                   */
/*                   Montage Technology (Shanghai) Inc.                     */
/*                          All Rights Reserved                             */
/****************************************************************************/
/*
 * Filename:      M88TS2808.c
 *
 * Description:   Montage M88TS2808 Digital Satellite Tuner IC driver.
 *                
 *
 * Author:        Daniel Zhu
 *
 * Henry Han	07-08-2008
 * Modify this file for ali_dvbs_v4 project
 
 * Version:       1.06            //add 3MHz filter BW for low symbol rate < 6.5MS/s  
 * Date:          2008-8-11
/****************************************************************************/
#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <bus/i2c/i2c.h>
#include <osal/osal_task.h>
#include <osal/osal_timer.h>
#include "nim_m88ts2808.h"



#define M88TS2808_i2c_addr   	0xc0		/* M88TS2808 i2c address */
#define M88TS2808_FXTAL      	27			/* Crystal Frequency of M88TS2808 used, unit: MHz , range: 16.0 - 32.0 */

#define M88TS2808_RFBYPASS_ON    1			/* If you need loop through function, please set it to 1 */
#define M88TS2808_CKOUT_ON       1			/* If you need clock out from tuner to demodulator use function, please set it to 1 */
#define M88TS2808_CKOUT_DIV      0x02		/* The clock out division, when M88TS2808_CKOUT_ON == 1 ,the  Fckout =  M88TS2808_FXTAL / M88TS2808_CKOUT_DIV  */
											/* M88TS2808_CKOUT_DIV can set from 0x00 to 0x1f */

struct QPSK_TUNER_CONFIG_EXT * stM88ts2808_dev_id[MAX_TUNER_SUPPORT_NUM] = {NULL};
static UINT32 stM88ts2808_tuner_cnt = 0;
#define TUNER_DELAY_MS(a) osal_delay((a) * 1000)

/*****************************************************************************
* INT32 nim_m88ts2808_read(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner read register from m88ts2808
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2118
*  Parameter2: UINT8* buff		: Read data buffer
*  Parameter3: UINT8 len		: Read length
*
* Return Value: INT32			: Result
*****************************************************************************/
static INT32 nim_m88ts2808_read(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	buff[0] = reg;
	return i2c_write_read(i2c_type_id, base_addr, buff, 1 , len);
}

/*****************************************************************************
* INT32 nim_m88ts2808_write(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner write register from m88ts2808
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2118
*  Parameter2: UINT8* buff		: write data buffer
*  Parameter3: UINT8 len		: write length
*
* Return Value: INT32			: Result
*****************************************************************************/
static INT32 nim_m88ts2808_write(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	UINT8  i, buffer[8];

	if (len > 7)
	{
	    return ERR_FAILUE;
	}
	buffer[0] = reg;
	for (i = 0; i < len; i++)
	{
	    buffer[i + 1] = buff[i];
	}
	return i2c_write(i2c_type_id, base_addr, buffer, len+1);
}

static INT32 M88TS2808WriteReg(UINT8 reg, UINT8 data)
{
	return nim_m88ts2808_write(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr,  reg, &data, 1);
}
static INT32 M88TS2808ReadRegBurst(UINT8 reg,UINT8* buff, UINT8 len)
{
	return nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, reg, buff, len);
}
/*****************************************************************************
* INT32 nim_m88ts2808_init(UINT32* tuner_id, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
*
* Tuner m88ts2808 Initialization
*
* Arguments:
*  Parameter1: UINT32* tuner_id
*  Parameter2: struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config		: pointer for Tuner configuration structure
*
* Return Value: INT32			: Result
*****************************************************************************/
static UINT8 init_act[2] = {0, 0};

INT32 nim_m88ts2808_init(UINT32* tuner_id, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
{
	struct QPSK_TUNER_CONFIG_EXT * stm88ts2808_ptr = NULL;

	M88TS2808_DEBUG("enter nim_m88ts2808_init \n");
	
	/* check Tuner Configuration structure is available or not */
	if (ptrTuner_Config == NULL||stM88ts2808_tuner_cnt>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	
	stm88ts2808_ptr = (struct QPSK_TUNER_CONFIG_EXT*)MALLOC(sizeof(struct QPSK_TUNER_CONFIG_EXT));
	if(!stm88ts2808_ptr)
		return ERR_FAILUE;
	
	stM88ts2808_dev_id[stM88ts2808_tuner_cnt] = stm88ts2808_ptr;
	MEMCPY(stm88ts2808_ptr, ptrTuner_Config, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	*tuner_id = stM88ts2808_tuner_cnt;
	stM88ts2808_tuner_cnt++;

	return SUCCESS;
}

/***************************************************************************/
/* Function to Initialize the M88TS2808 */
/***************************************************************************/
static void M88TS2808Initial(void)
{ 
	UINT8 buf;

	if(M88TS2808_CKOUT_ON)
	{
		M88TS2808WriteReg(0x42,0x73);
		M88TS2808WriteReg(0x05,M88TS2808_CKOUT_DIV);
	}
	else
	{
		M88TS2808WriteReg(0x42,0x63);
	}

	if(M88TS2808_RFBYPASS_ON)
	{
		M88TS2808WriteReg(0x62,0xf5);
	}
	else
	{
		M88TS2808WriteReg(0x62,0xb5);
	}

	M88TS2808WriteReg(0x07,0x02);
	M88TS2808WriteReg(0x10,0x00);
	M88TS2808WriteReg(0x60,0x79);
	M88TS2808WriteReg(0x08,0x01);
	M88TS2808WriteReg(0x00,0x01);
}

/*************************************************************************/
/*	Function to Set the M88TS2808 */

/*	fPLL:   Frequency        			unit: MHz  */
/*	fSym:   SymbolRate       			unit: KS/s */
/*  return: Frequency offset of PLL  	uint: KHz */
/************************************************************************/
INT32 nim_m88ts2808_control(UINT32 tuner_id, UINT32 fPLL, UINT32 fSym)
{	
	UINT8	buf;
	UINT8	div4, changePLL;
	UINT8	K, lpf_mxdiv, divMax, divMin;
	UINT16   gdiv28;
	UINT32	N, lpf_gm, f3dB;

	struct QPSK_TUNER_CONFIG_EXT * stM88TS2808_ptr = NULL;
	
	if (tuner_id>=stM88ts2808_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	stM88TS2808_ptr = stM88ts2808_dev_id[tuner_id];

	if (init_act[tuner_id] == 0) //do the init here.
	{
		M88TS2808Initial();	//Initialize the tuner
		//init_act[tuner_id] = 1;
	}
#ifdef HENRY_TEST_TUNER_I2C
	if (init_act[1] == 0) //do the test
	{
		nim_m88ts2808_i2c_test();
		init_act[1] = 1;
	}
#endif	

       if(fSym == 0)                     //added by walter 080813 for autoscan when fSym== 0 
	   	fSym=40000;
	   
	div4 = 0;
	changePLL = 0;
	
	//  set the PLL	
	K = (UINT8)((M88TS2808_FXTAL+1)/2 - 8);

        if (fPLL < 1146 ) 
	{
		M88TS2808WriteReg(0x10, 0x11); 
		div4 = 1;
		N = (UINT32) (((fPLL * 8 * (K + 8) ) / M88TS2808_FXTAL + 1)/2 - 1024);
	}
	else 
	{	
		M88TS2808WriteReg(0x10, 0x01);
		N = (UINT32) (((fPLL * 4 * (K+8) ) / M88TS2808_FXTAL + 1)/2 - 1024);
	}

	buf = (UINT8) ((N >>8) & 0x0f);  
	M88TS2808WriteReg(0x01, buf);  

	buf = (UINT8) (N & 0xff);           
	M88TS2808WriteReg(0x02, buf); 

	buf = (UINT8) K;
	M88TS2808WriteReg(0x03,buf);

	M88TS2808WriteReg(0x51, 0x0f);   
	M88TS2808WriteReg(0x51, 0x1f);   
	M88TS2808WriteReg(0x50, 0x10);       
	M88TS2808WriteReg(0x50, 0x00); 

	TUNER_DELAY_MS(5); 

	M88TS2808ReadRegBurst(0x66,&buf,1);
	changePLL = (UINT8)(((buf & 0x80) >> 7) != div4); 

	if(changePLL)
	{
			M88TS2808WriteReg(0x10, 0x11); 
			div4 = 1;
			N = (UINT32) (((fPLL * 8 * (K + 8) ) / M88TS2808_FXTAL + 1)/2 - 1024);

		buf = (UINT8) ((N >>8) & 0x0f);  
		M88TS2808WriteReg(0x01, buf);
		buf = (UINT8) (N & 0xff);           
		M88TS2808WriteReg(0x02, buf); 
		M88TS2808WriteReg(0x51, 0x0f);   
		M88TS2808WriteReg(0x51, 0x1f);   
		M88TS2808WriteReg(0x50, 0x10);       
		M88TS2808WriteReg(0x50, 0x00); 

		TUNER_DELAY_MS(5); 
	}

	M88TS2808WriteReg(0x51, 0x17);   
	M88TS2808WriteReg(0x51, 0x1f);   
	M88TS2808WriteReg(0x50, 0x08);       
	M88TS2808WriteReg(0x50, 0x00); 

	TUNER_DELAY_MS(5);  

		M88TS2808ReadRegBurst(0x3d,&buf,1);
		buf = buf&0x0f;
		if((buf > 4) && (buf< 15 ))
		{ buf = buf - 3;
                      if(buf < 4) buf = 4; 
			buf = ((buf  << 3) | 0x01) & 0x79;
			M88TS2808WriteReg(0x60,buf);

			M88TS2808WriteReg(0x51, 0x17);   
			M88TS2808WriteReg(0x51, 0x1f);   
			M88TS2808WriteReg(0x50, 0x08);       
			M88TS2808WriteReg(0x50, 0x00);
		}

	// set the LPF

	f3dB = (UINT32) (fSym*4/5 + 2000); 
	
	if( fSym < 6500 )   f3dB += 3000; 	 // When the demodulator set the frequncy offset 3MHz at low symbol rate,the filter is add 3MHz.

	if (f3dB < 7000)                     
		f3dB = 7000;
	if(f3dB > 40000) 	
		f3dB = 40000;

	gdiv28 = (UINT16) ((M88TS2808_FXTAL*1694 + 500)/1000); 

	buf = (UINT8) gdiv28;
	M88TS2808WriteReg(0x04, buf); 

	M88TS2808WriteReg(0x51, 0x1b);   
	M88TS2808WriteReg(0x51, 0x1f);   
	M88TS2808WriteReg(0x50, 0x04);       
	M88TS2808WriteReg(0x50, 0x00);  

	TUNER_DELAY_MS(5);  

	M88TS2808ReadRegBurst(0x26,&buf,1);

	gdiv28 = (UINT16) (gdiv28 * 207 / (buf * 2 + 151));   
	divMax = (UINT8) (gdiv28* 135 / 100);                           
	divMin = (UINT8) (gdiv28* 78 / 100);

	if(divMax>63)	
		divMax=63;

	lpf_gm = (UINT32) ((f3dB  * gdiv28 * 2 / 2766 / M88TS2808_FXTAL + 1)/2);

	if (lpf_gm>23)  
		lpf_gm = 23;
	if (lpf_gm<1)   
		lpf_gm = 1;

	lpf_mxdiv = (UINT8) ((lpf_gm * 2766 * 2  * M88TS2808_FXTAL  / f3dB + 1)/2);

	if (lpf_mxdiv<divMin) 
	{
		lpf_gm++;
		lpf_mxdiv = (UINT8) ((lpf_gm * 2766 * 2 * M88TS2808_FXTAL  / f3dB + 1)/2);
	}

	if (lpf_mxdiv>divMax) 
	{
		lpf_mxdiv=divMax;
	}

	buf = (UINT8) lpf_mxdiv;
	M88TS2808WriteReg(0x04, buf);   

	buf = (UINT8) lpf_gm;
	M88TS2808WriteReg(0x06, buf);   

	M88TS2808WriteReg(0x51, 0x1b);   
	M88TS2808WriteReg(0x51, 0x1f);   
	M88TS2808WriteReg(0x50, 0x04);       
	M88TS2808WriteReg(0x50, 0x00); 

	TUNER_DELAY_MS(5);		

	M88TS2808WriteReg(0x51, 0x1e);   
	M88TS2808WriteReg(0x51, 0x1f);   
	M88TS2808WriteReg(0x50, 0x01);       
	M88TS2808WriteReg(0x50, 0x00); 

	TUNER_DELAY_MS(50);
	TUNER_DELAY_MS(50);

#ifdef WALTER_CHECK_GAIN
	{
		UINT8  gain1,gain2;

		M88TS2808ReadRegBurst(0x3d, &gain1, 1);
		M88TS2808ReadRegBurst(0x21, &gain2, 1);
		M88TS2808_DEBUG("********************0x3d, gain1 is 0x%x\n", gain1 & 0x1f);
		M88TS2808_DEBUG("********************0x21,gain2 is 0x%x\n", gain2 & 0x1f);
	}
#endif
  
	return SUCCESS;
}

INT32 nim_m88ts2808_status(UINT32 tuner_id, UINT8 *lock)
{
#if 0
	UINT8  agc, gain,gain1,gain2;
	static UINT8 pregain2 = 0;

	M88TS2808ReadRegBurst(0x3d, &gain1, 1);
	M88TS2808ReadRegBurst(0x21, &gain2, 1);
	gain1 = gain1 & 0x1f;
	gain2 = gain2 & 0x1f;

	if(gain1 < 0)
		gain1 = 0;
	
	if(gain1 > 15)
		gain1 = 15;
	
	if(gain2 < 0)
		gain2 = 0;
	
	if(gain2 > 13)
		gain2 = 13;

	if(gain2 < 7 && *lock == 0)
		gain2 = pregain2;
	
	pregain2 = gain2;

	gain =  gain1 * 2 + gain2 * 3; 

	if(gain > 64)
		gain = 64;

	if(*lock == 0 && gain > 50)    //no cable 
		agc = 129;
	else
		agc = 64 - gain;

#ifdef WALTER_CHECK_GAIN
	{
		static cnt = 0;

		if (cnt ++ >= 50)
		{
			M88TS2808_DEBUG("agc: %d gain:%d, gain1:%d, gain2:%d, lock:%d\n", agc, gain, gain1, gain2, *lock);
			cnt = 0;
		}
	}
#endif

	*lock = agc;
#else
	*lock = 1;
#endif

	return SUCCESS;
	
}

void nim_m88ts2808_i2c_test(void)
{
	char buff0[20];
	char buff1[20];
	char buff2[20];
	char buff3[20];

	const char write_bytes[8] = {0xde, 0x29, 0x1b, 0x1f, 0x24, 0x30, 0, 0};

	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x00, buff0, 10);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x10, buff1, 10);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x50, buff2, 2);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x60, buff3, 2);

	M88TS2808WriteReg(0x04, write_bytes[0]);
	M88TS2808WriteReg(0x06, write_bytes[1]);   
	M88TS2808WriteReg(0x51, write_bytes[2]);   
	M88TS2808WriteReg(0x51, write_bytes[3]);   
	M88TS2808WriteReg(0x50, write_bytes[4]);       
	M88TS2808WriteReg(0x50, write_bytes[5]); 

	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x00, buff0, 10);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x10, buff1, 10);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x50, buff2, 2);
	nim_m88ts2808_read(stM88ts2808_dev_id[0]->i2c_type_id, stM88ts2808_dev_id[0]->cTuner_Base_Addr, \
		0x60, buff3, 2);

	return;	
}


